/// 引入子模块
mod content;
mod utils;
use content::project;
use utils::logger;
use crate::project::CreationOptions;

///处理错误类型
use anyhow::Result;
/// 命令行参数解析
use clap::{ builder::{ EnumValueParser, ValueHint }, Parser, Subcommand, ValueEnum };
/**
 * 跨平台路径操作。
    该模块提供两种类型：PathBuf 和 Path（类似于 String 和 str），用于抽象地处理路径。
 */
use std::path::PathBuf;

use dialoguer::{ console::Term, theme::ColorfulTheme, Select };


#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum UIDesign {
    Antd,
    Arco,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum CSSPreProcessors {
    Sass,
    Less,
    Stylus,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum Hook {
    AHooks,
    CustomizeHook,
    AHooksAndCustomize,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum StateManagement {
    Redux,
    Zustand,
    Recoil,
    Jotai,
    MobX,
    Valtio,
}

/// 用于描述命令行界面的结构体
#[derive(Parser)]
/// 这个宏用于定义命令行工具的元信息，包括名称、作者、版本和关于信息。
#[command(
    name = "f_cli",
    author,
    version,
    about,
    args_conflicts_with_subcommands = true // 表示在存在子命令时，不允许主命令使用与子命令同名的参数。
)]
struct Cli {
    /// 定义子命令
    #[command(subcommand)]
    command: Option<Commands>,

    #[arg(help = "可选参数，用于指定新项目的名称", value_hint = ValueHint::DirPath)]
    name: Option<String>,
}

/// 定义了一个名为 Commands 的枚举
#[derive(Subcommand)]
enum Commands {
    /// 创建一个新的项目
    Create {
        // 处理非交互式模式，通过配置参数，直接生成一个默认的项目
        #[arg(
            short = 'c',
            long = "cli",
            name = "非交互式模式",
            help = "通过命令行参数配置项目",
            requires = "ui",
            requires = "style",
            requires = "hook",
            requires = "state",
        )]
        cli_mode: bool,

        #[arg(help = "项目的名称", value_hint = ValueHint::DirPath)]
        name: String,

        #[arg(
            short = 'u',
            long = "ui",
            name = "ui",
            help = "配置UI库",
            value_name = "UI库",
            value_parser = EnumValueParser::<UIDesign>::new(),
            ignore_case = true
        )]
        ui_design: Option<UIDesign>,
        #[arg(
            short = 's',
            long = "style",
            name = "style",
            help = "配置样式",
            value_name = "样式信息",
            value_parser = EnumValueParser::<CSSPreProcessors>::new(),
            ignore_case = true
        )]
        css_pre_processors: Option<CSSPreProcessors>,
        #[arg(
            short = 'H',
            long = "hook",
            name = "hook",
            help = "配置Hook",
            value_name = "hook信息",
            value_parser = EnumValueParser::<Hook>::new(),
            ignore_case = true
        )]
        hook: Option<Hook>,
        #[arg(
            short = 'S',
            long = "state_management",
            name = "state",
            help = "选择状态管理",
            value_name = "state信息",
            value_parser = EnumValueParser::<StateManagement>::new(),
            ignore_case = true
        )]
        state_management: Option<StateManagement>,
    },
    // TODO ，还可以做更新命令
}

/// 项目主入口
///
/// 用单个命令快速创建 React 应用
fn main() -> Result<()> {
    //  使用 clap 提供的 parse 方法解析命令行参数，得到一个 Cli 结构体的实例。
    let cli = Cli::parse();
    // 检查项目是否是最新版本
    project::check_cli_version()?;
    // 根据用户是否提供自命令来处理
    match cli.command {
        // - 如果有子命令，则根据子命令执行相应的逻辑；
        Some(command) => {
            match command {
                // 如果命令是 Create 子命令，则调用 create_project 函数，传递 cli_mode 和 name 作为参数
                Commands::Create {
                    cli_mode,
                    name,
                    ui_design,
                    css_pre_processors,
                    hook,
                    state_management,
                } =>
                    create_project(
                        cli_mode,
                        name,
                        ui_design,
                        css_pre_processors,
                        hook,
                        state_management
                    )?,
                // todo:处理更新的逻辑
            }
        }
        // - 如果没有子命令，则根据是否提供了 name 参数执行相应的逻辑。
        None => {
            match cli.name {
                // 提供name，调用 create_project 函数，传递 false 作为 cli_mode 参数，并将提供的 name 作为项目名称
                Some(name) => create_project(
                    false, 
                    name, 
                    None, 
                    None, 
                    None, 
                    None
                )?,
                // 未提供name，则打印错误信息
                None => logger::error("未提供项目名称，请提供一个项目名称。"),
            };
        }
    }
    Ok(())
}

/// 实际构建项目的函数
/// 这个函数接受两个参数，
/// 1. 一个布尔值 cli_mode 表示是否以非交互模式创建项目
/// 2. 以及一个字符串 project_name 表示项目的名称
fn create_project(
    cli_mode: bool,
    project_name: String,
    ui_design: Option<UIDesign>,
    css_pre_processors: Option<CSSPreProcessors>,
    hook: Option<Hook>,
    state_management: Option<StateManagement>
) -> anyhow::Result<()> {
    // 尝试在一个已存在的目录中创建项目，则抛出错误。
    if PathBuf::from(&project_name).exists() {
        logger::error(&format!("无法创建项目: {:#?} 已经存在.", PathBuf::from(&project_name)));
        return Ok(());
    }
    logger::command_msg("开始创建项目...");

    let ui = match ui_design {
        Some(ui_design) => ui_design,
        None => {
            if cli_mode {
                panic!("Fatal: UI库未指定");
            }
            logger::multiselect_msg("选择一个UI库:");
            logger::message("使用上/下箭头进行选择，使用空格或回车键确认。");
            let items = vec!["antd", "arco"];
            let selection = Select::with_theme(&ColorfulTheme::default())
                .items(&items)
                .default(0)
                .interact_on_opt(&Term::stderr())?;

            match selection {
                Some(0) => UIDesign::Antd,
                Some(1) => UIDesign::Arco,
                _ => panic!("Fatal: UI库指定失败."),
            }
        }
    };

    let css = match css_pre_processors {
        Some(css_pre_processors) => css_pre_processors,
        None => {
            if cli_mode {
                panic!("Fatal: CSS预处理库未指定");
            }
            logger::multiselect_msg("选择一个CSS预处理库:");
            logger::message("使用上/下箭头进行选择，使用空格或回车键确认。");
            let items = vec!["scss", "less", "stylus"];
            let selection = Select::with_theme(&ColorfulTheme::default())
                .items(&items)
                .default(0)
                .interact_on_opt(&Term::stderr())?;

            match selection {
                Some(0) => CSSPreProcessors::Sass,
                Some(1) => CSSPreProcessors::Less,
                Some(2) => CSSPreProcessors::Stylus,
                _ => panic!("Fatal: CSS预处理库指定失败."),
            }
        }
    };

    let _hook = match hook {
        Some(hook) => hook,
        None => {
            if cli_mode {
                panic!("Fatal: Hook信息未指定");
            }
            logger::multiselect_msg("选择一个Hook解决方案:");
            logger::message("使用上/下箭头进行选择，使用空格或回车键确认。");
            let items = vec!["ahook", "customizeHook", "ahookAndCustomize"];
            let selection = Select::with_theme(&ColorfulTheme::default())
                .items(&items)
                .default(0)
                .interact_on_opt(&Term::stderr())?;

            match selection {
                Some(0) => Hook::AHooks,
                Some(1) => Hook::CustomizeHook,
                Some(2) => Hook::AHooksAndCustomize,
                _ => panic!("Fatal: Hook解决方案指定失败."),
            }
        }
    };
    let state = match state_management {
        Some(state_management) => state_management,
        None => {
            if cli_mode {
                panic!("Fatal: state_management信息未指定");
            }
            logger::multiselect_msg("选择一个状态管理解决方案:");
            logger::message("使用上/下箭头进行选择，使用空格或回车键确认。");
            let items = vec![
                "ReducerBasedRedux",
                "ReducerBasedZustand",
                "AtomBasedRecoil",
                "AtomBasedJotai",
                "MutableBasedMobX",
                "MutableBasedValtio"
            ];
            let selection = Select::with_theme(&ColorfulTheme::default())
                .items(&items)
                .default(0)
                .interact_on_opt(&Term::stderr())?;

            match selection {
                Some(0) => StateManagement::Redux,
                Some(1) => StateManagement::Zustand,
                Some(2) => StateManagement::Recoil,
                Some(3) => StateManagement::Jotai,
                Some(4) => StateManagement::MobX,
                Some(5) => StateManagement::Valtio,
                _ => panic!("Fatal: state解决方案指定失败."),
            }
        }
    };

    // 选择UI库
    // 调用project创建项目
    project::create(
        project_name.as_ref(),
        CreationOptions {
            state,
            _hook,
            css,
            ui,
            cli_mode,
        }
    )?;

    logger::project_created_msg(project_name);

    Ok(())
}
